/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * MainForm.java
 *
 * Created on Jan 23, 2011, 3:29:10 PM
 */

package UI;

import BusLib.Station;
import Core.Coordinates;
import Core.ILayer;
import Core.IMapProvider;
import Core.IProjection;
import Core.LayerType;
import Core.Marker;
import Core.Node;
import Core.ObjectId;
import Core.Point;
import Core.Region;
import Core.Region2D;
import Core.Street;
import Core.StreetType;
import Core.TileInfo;
import Projection.MercatorProjection;
import Rendering.Action;
import Rendering.ActionType;
import Rendering.IViewerHost;
import Rendering.IVisual;
import Rendering.Info.MarkerInfo;
import Rendering.InfoType;
import Rendering.MapViewer;
import Rendering.View;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import org.restlet.resource.ClientResource;

/**
 *
 * @author Gratian2
 */
public class MainForm extends javax.swing.JFrame implements IViewerHost, IMapProvider {


    static class RouteInfo {
        private String name_;
        private int id_;

        public RouteInfo() {}

        public RouteInfo(String name, int id) {
            name_ = name;
            id_ = id;
        }

        @Override
        public String toString() {
            return Name();
        }

        public String Name() {
            return name_;
        }

        public void SetName(String name_) {
            this.name_ = name_;
        }

        public int Id() {
            return id_;
        }

        public void SetId(int id_) {
            this.id_ = id_;
        }
    }
    
    private static double MARKER_SIZE = 40;
    private static Color[] MARKER_COLORS = new Color[] {
        new Color(0, 167, 255), // Albastru
        new Color(166, 227, 4), // Verde
        new Color(250, 148, 0), // Portocaliu
    };

    private static float MARKER_WIDTH = 3;
    Stroke markerStroke_ = new BasicStroke(MARKER_WIDTH);

    class MarkerVisual implements IVisual {
        private Marker marker_;
        private Point position_;
        private Color color_;

        /*
        * Constructori.
        */
        public MarkerVisual(Marker marker,
                            Point position, Color color) {
            marker_ = marker;
            position_ = position;
            color_ = color;
        }

        /*
        * Metode publice.
        */
        public Marker Marker() { return marker_; }
        public Point Position() { return position_; }
        public void SetPosition(Point value) { position_ = value; }

        public Color Color() { return color_; }
        public void SetColor(Color value) { color_ = value; }

        public void Draw(Graphics2D g, View view) {
            double x = position_.X() - view.Bounds().Left();
            double y = position_.Y() - view.Bounds().Top();
             g.setColor(color_);

            // Seteaza grosimea liniei.
               g.setStroke(markerStroke_);

            double centerX = x - (MARKER_SIZE / 2);
            double centerY = y - (MARKER_SIZE / 2);
            g.drawOval((int)centerX, (int)centerY, (int)MARKER_SIZE, (int)MARKER_SIZE);
        }
    }

    MapViewer viewer_;
    BusLib.Map map_;
    StationLayer stations_;
    boolean debug_;
    BufferedImage marker_;
    int selectedRoute_ = -1;
    Marker selectedS1_;

    StreetLayer streets_;
    HashMap<Integer, ArrayList<Street>> streetMap_;

    /** Creates new form MainForm */
    public MainForm() {
        initComponents();
        streetMap_ = new HashMap<Integer, ArrayList<Street>>();
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
            .addKeyEventDispatcher(new KeyEventDispatcher()
            {
                public boolean dispatchKeyEvent(KeyEvent e) {
                    if (e.getID() == KeyEvent.KEY_PRESSED) {
                        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                            viewer_.Pan(new Core.Point(-512, 0), 500);
                            return true;
                        }
                        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                            viewer_.Pan(new Core.Point(512, 0), 500);
                            return true;
                        }
                        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                            viewer_.Pan(new Core.Point(0, 512), 500);
                            return true;
                        }
                        if (e.getKeyCode() == KeyEvent.VK_UP) {
                            viewer_.Pan(new Core.Point(0, -512), 500);
                            return true;
                        }
                        else if(e.getKeyCode() == KeyEvent.VK_D && e.isControlDown()) {
                            viewer_.SetDebug(!debug_);
                            debug_ = !debug_;
                        }
                    }
                    return false;
                }
            });

    }

    void LoadStations() {
        ClientResource resource = new ClientResource("http://localhost:8182/station");
        BusLib.ObjectList stations = resource.get(BusLib.ObjectList.class);
        List<BusLib.StationInfo> list = (List<BusLib.StationInfo>)stations.getList();

        stations_ = new StationLayer();

        for(BusLib.StationInfo s : list) {
            Coordinates c = new Coordinates(s.Coordinates().Latitude(),
                                            s.Coordinates().Longitude());
            Marker m = new Marker(new ObjectId(s.Id()), c, s.Name());
            stations_.Makers().add(m);
        }

        viewer_.AddLayer(stations_);
        viewer_.ShowLayer(stations_, 0);
    }

    void LoadMap() {
        ClientResource resource = new ClientResource("http://localhost:8182/map");
        map_ = resource.get(BusLib.Map.class);
        viewer_ = new MapViewer(this);
        viewer_.setBackground(Color.red);
        viewer_.setBounds(260, 32, 512, 512);
        viewer_.SetSize(512, 512);
        viewer_.LoadMap(this);
        this.getContentPane().add(viewer_);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel2 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jList2 = new javax.swing.JList();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        jToolBar1 = new javax.swing.JToolBar();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        jPanel2.setBackground(new java.awt.Color(241, 240, 240));

        jLabel1.setText("Routes");

        jList1.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                jList1ValueChanged(evt);
            }
        });
        jScrollPane1.setViewportView(jList1);

        jLabel2.setText("Bus");

        jList2.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        jScrollPane2.setViewportView(jList2);

        jLabel3.setText("Schedule");

        jLabel4.setText("Station:");

        jLabel5.setText("Station:");

        jLabel6.setText("Street:");

        jLabel7.setText("Station:");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel2)
                .addContainerGap(236, Short.MAX_VALUE))
            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 263, Short.MAX_VALUE)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel3)
                .addContainerGap(210, Short.MAX_VALUE))
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel6)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(jLabel7))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel4)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(jLabel5)))
                .addContainerGap(167, Short.MAX_VALUE))
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addContainerGap(219, Short.MAX_VALUE))
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 263, Short.MAX_VALUE)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(jLabel5))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel6)
                    .addComponent(jLabel7))
                .addGap(5, 5, 5)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 102, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jLabel3)
                .addGap(18, 18, 18)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 295, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        jToolBar1.setRollover(true);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(552, Short.MAX_VALUE))
            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 815, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        LoadMap();
        LoadStations();
    }//GEN-LAST:event_formWindowOpened

    private void jList1ValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jList1ValueChanged
        RouteInfo r = (RouteInfo)jList1.getModel().getElementAt(evt.getFirstIndex());
        SelectRoute(r.Id());
    }//GEN-LAST:event_jList1ValueChanged

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainForm().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JList jList1;
    private javax.swing.JList jList2;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JToolBar jToolBar1;
    // End of variables declaration//GEN-END:variables

    public void ActionPerformed(Action action) {
        if(action.Type() == ActionType.ObjectSelected) {
            if(action.ObjectInfo().Type() == InfoType.Marker) {
                MarkerInfo m =(MarkerInfo)action.ObjectInfo();
                if(m.Marker() == selectedS1_) return;

                int id = m.Marker().ID().Id();

                if(selectedS1_ != null) {
                    viewer_.Overlay().RemoveOverlay(selectedS1_);
                }

                viewer_.Overlay().AddOverlay(m.Marker(), 
                                 new MarkerVisual(m.Marker(), m.Position(), MARKER_COLORS[1]));
                selectedS1_ = m.Marker();

                try {
                    ClientResource resource = new ClientResource("http://localhost:8182/station/" + id);
                    BusLib.Station stations = resource.get(BusLib.Station.class);

                    jList1.removeAll();
                    DefaultListModel list = new DefaultListModel();

                    for(BusLib.Route r : stations.Routes()) {
                        list.addElement(new RouteInfo(r.Name(), r.Id()));
                    }

                    jList1.setModel(list);

                    CreateStreets(stations);
                }
                catch(Exception e) {
                    e.printStackTrace();
                }
            }
            else {
                if(selectedRoute_ != -1) {}
            }
        }
    }

    private void CreateStreets(Station stations) {
        if(streets_ != null) {
            viewer_.RemoveLayer(streets_);
        }

        selectedRoute_ = -1;
        streets_ = new StreetLayer();

        for(BusLib.Route r : stations.Routes()) {
            ArrayList<Street> list = new ArrayList<Street>();

            for(BusLib.Link l : r.Links()) {
                for(BusLib.Street s : l.Streets()) {
                    Street cs = new Street(new ObjectId(s.Id()), StreetType.Street,
                                          s.Name(), null, null);

                    for(BusLib.Coordinates c : s.Points()){
                        cs.AddCoordinate(new Coordinates(c.Latitude(), c.Longitude()));
                    }

                    list.add(cs);
                    streets_.Streets().add(cs);
                }
            }

            streetMap_.put(r.Id(), list);
        }

        viewer_.AddLayer(streets_);
        viewer_.HideLayer(streets_, 0);
        viewer_.ShowLayer(streets_, 1000);
    }

    void SelectRoute(int r) {
        if(selectedRoute_ != -1) {
            DeselectRoute(selectedRoute_);
        }

        ArrayList<Street> list = (ArrayList<Street>)streetMap_.get(r);

        for(Street s : list) {
            s.SetType(StreetType.Boulevard);
        }

        viewer_.Repaint();
        selectedRoute_ = r;
    }

    void DeselectRoute(int r) {
        ArrayList<Street> list = (ArrayList<Street>)streetMap_.get(r);

        for(Street s : list) {
            s.SetType(StreetType.Street);
        }

        viewer_.Repaint();
        selectedRoute_ = -1;
    }

    public String Name() {
        return "";
    }

    public int ZoomLevels() {
        return map_.Levels();
    }

    public Region MapBounds() {
        return new Region(map_.TopLeft().Latitude(), map_.TopLeft().Longitude(),
                          map_.BottomRight().Latitude(), map_.BottomRight().Longitude());
    }

    public Region2D MapBounds(int zoomLevel) {
        Region r = MapBounds();
        return new Region2D(r.TopLeft().Latitude(), r.TopLeft().Longitude(),
                            r.BottomRight().Latitude() * (zoomLevel+1),
                            r.BottomRight().Longitude() * (zoomLevel + 1));
    }

    public IProjection Projection() {
        return new MercatorProjection(this);
    }

    public int LayerCount() {
        return 0;
    }

    public boolean HasLayer(LayerType layerType) {
        return false;
    }

    public ILayer GetLayer(ObjectId layerId) {
        return null;
    }

    public Iterator<ILayer> GetLayerIterator() {
        return null;
    }

    public double TileSize() {
        return 256;
    }

    public int TileCount(int zoomLevel) {
        return 0;
    }

    public TileInfo GetTile(Point point, int zoomLevel) {
        return null;
    }

    public void GetTiles(Region2D region, int zoomLevel, List<TileInfo> list) {
        
    }

    public Image LoadTile(ObjectId tileId) {
        return null;
    }

    public int NodeNumber() {
        return 0;
    }

    public Iterator<Node> GetNodeIterator() {
        return null;
    }

    public Node GetNode(ObjectId nodeID) {
        return null;
    }

    public int StreetCount() {
        return streets_ != null ? streets_.Streets().size() : 0;
    }

    public Iterator<Street> GetStreetIterator() {
        return streets_ != null ? streets_.Streets().iterator() : null;
    }

    public Street GetStreet(ObjectId streetID) {
        if(streets_ == null) return null;
        for(Street s : streets_.Streets()) {
            if(s.Id() == streetID) return s;
        }

        return null;
    }

    public Core.Color StreetColor(StreetType streetType) {
        if(streetType == StreetType.Street) {
            return new Core.Color(0,0,255);
        }
        else return new Core.Color(255,0,0);
    }

    public int MarkerCount(ObjectId layerID) {
        return stations_.Makers().size();
    }

    public Iterator<Marker> GetMarkerIterator(ObjectId layerID) {
        return stations_.Makers().iterator();
    }

    public Marker GetMarker(ObjectId markerID, ObjectId layerID) {
        for(Marker m : stations_.Makers()) {
            if(m.ID().equals(markerID)) return m;
        }

        return null;
    }

    public Image LoadMarkerIcon(ObjectId layerID) throws IOException {
        if(marker_ != null) return marker_;

        try {
                marker_ = ImageIO.read(new File("D:\\station.png"));
        }
        catch(Exception e) {
        }
        
        return marker_;
    }

    public void Load(String location) throws IOException, IllegalStateException {
        
    }

    public void Save(String location) throws IOException {
        
    }
}
